<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app">
      <p>vue响应式得本质</p>
      <p v-text="msg"></p>
      <p v-text="msg"></p>
      <p v-text="msg"></p>
      <input type="text" v-model="msg" />

      <p v-text="info"></p>
      <p v-text="info"></p>
      <p v-text="info"></p>
      <input type="text" v-model="info" />

      <p v-text="num"></p>
      <input type="text" v-model="num" />
    </div>
    <script>
      /*
        vue的响应式原理 Object.defineProperty 对数据完成的数据劫持操作(get,set),
        并基于发布者订阅者模式，完成响应式数据和页面元素的同步
      */
      // 记录Map,用于记录变量和DOM的关系
      let mapDom = new Map();

      // 1. 提供了数据
      let obj = {
        msg: "msg",
        info: "info",
        num: 1000,
      };

      // 2. 将数据转为响应式
      //    2.1. vue对数据仓库进行深度克隆(模拟使用简单的浅克隆)
      let $data = { ...obj };
      //    2.2. 循环所有的数据，对提供数据进行响应式转换(数据劫持-拦截对象的取值和赋值)
      for (let k in obj) {
        // 将普通对象的每个属性都改造成带有 get set 的响应式数据
        Object.defineProperty(obj, k, {
          get() {
            // console.log("get");
            return $data[k];
          },
          set(nv) {
            // console.log("set");
            $data[k] = nv;
            // 触发于该变量有关的页面DOM元素的更新(订阅)
            let doms = mapDom.get(k);
            for (let i = 0; i < doms.length; i++) {
              doms[i].innerHTML = obj[k];
            }
          },
        });
      }
      // 3. 查找了容器
      let appBox = document.querySelector("#app");

      // 4. 循环（data仓库）数据
      for (let k in obj) {
        // 基于vue得模板语法(指令插值表达式……)定义元素查询关键字
        let el = `[v-text=${k}]`;
        // console.log(el);
        // 基于容器，在容器内查找符合条件的相关元素
        let doms = appBox.querySelectorAll(el);
        // 将变量对应的DOM元素存入map(发布)
        mapDom.set(k, doms);
        // console.log(doms);
        // 循环元素完成对应的 数据内容的填充和渲染
        for (let i = 0; i < doms.length; i++) {
          doms[i].innerHTML = obj[k];
        }

        let el2 = `[v-model=${k}]`;
        let inputs = appBox.querySelectorAll(el2);
        for (let i = 0; i < inputs.length; i++) {
          inputs[i].value = obj[k];
          inputs[i].addEventListener("input", function () {
            obj[k] = this.value;
          });
        }
      }
    </script>
  </body>
</html>
